#ifndef SPL_WRAPWND
#define SPL_WRAPWND

#define DEFAULT_NOTIFYID    0x1
#define WM_NOTIFYICON       WM_USER + 0x101

#pragma warning(disable:4065)

//! Scroll structure
struct ScrollInfo
{
  POINT pos;
  POINT max;
};

//! Class to wrap window functions
struct DLLEXPORT WrapWnd
{
private:
#pragma region Internal Declarations
  //! Window handle
  HWND  mHandle;
#pragma endregion

public:
#pragma region Constructors & Destructors
  //! Window constructor
  SPLINL WrapWnd(HWND hWnd) : mHandle(hWnd)
  {
  }
  //! Window base constructor
  SPLINL WrapWnd() : mHandle(NULL)
  {
  }
#pragma endregion

#pragma region Management
  //! Creates window
  //! hInstance:      window instance
  //! wndClass:       window class name
  //! wndName:		    window title
  //! wndBounds*:		  window dimentions
  //! dwStyle:		    window style
  //! dwExStyle:		  window extended style
  //! hWndParent:		  parent window handle
  //! hMenu:		      menu handle (if different from window class)
  //! Returns:        true if success, false if failure
  SPLINL BOOL create(
    HINSTANCE hInstance,
    LPCTSTR   wndClass,
    LPCTSTR   wndName,
    const Geom::BOUNDS& wndBounds,
    DWORD     dwStyle     = WS_OVERLAPPEDWINDOW,
    DWORD     dwExStyle   = 0,
    HWND      hWndParent  = NULL,
    HMENU     hMenu       = NULL
    )
  {
    return CreateWindowEx(
      dwExStyle, wndClass, wndName, dwStyle,
      wndBounds.x, wndBounds.y, wndBounds.width, wndBounds.height,
      hWndParent, hMenu,
      hInstance, this) > 0;
  }

  //! Creates window
  //! hInstance:      window instance
  //! wndClass:       window class name
  //! wndName:		    window title
  //! x:		          window left bound
  //! y:              window top bound
  //! width:          window horizontal size
  //! height:         window vertical size
  //! dwStyle:		    window style
  //! dwExStyle:		  window extended style
  //! hWndParent:		  parent window handle
  //! hMenu:		      menu handle (if different from window class)
  //! Returns:        true if success, false if failure
  SPLINL BOOL create(
    HINSTANCE hInstance,
    LPCTSTR   wndClass,
    LPCTSTR   wndName,
    INT       x,
    INT       y,
    INT       width,
    INT       height,
    DWORD     dwStyle     = WS_OVERLAPPEDWINDOW,
    DWORD     dwExStyle   = 0,
    HWND      hWndParent  = NULL,
    HMENU     hMenu       = NULL
    )
  {
    return (mHandle = CreateWindowEx(
      dwExStyle, wndClass, wndName, dwStyle,
      x, y, width, height,
      hWndParent, hMenu,
      hInstance, this)) > 0;
  }

  //! Destroys this window
  LRESULT         destroy();
  //! Enter window message loop
  WPARAM          run(BOOL iRunThread = TRUE);
#pragma endregion

#pragma region Accessors
  //! Sends a message to this window
  SPLINL LRESULT send(UINT iMessage, WPARAM wParam, LPARAM lParam) const
  {
    return SendMessage(mHandle, iMessage, wParam, lParam);
  }
  //! Posts a message to this window
  SPLINL LRESULT post(UINT iMessage, WPARAM wParam, LPARAM lParam) const
  {
    return PostMessage(mHandle, iMessage, wParam, lParam);
  }

  //! Returns a window relative to this window
  SPLINL HWND getWindow(UINT iGWCommand) const
  {
    return GetWindow(mHandle, iGWCommand);
  }
  //! Returns the window preceding or following this window
  SPLINL HWND getNextWindow(UINT iGWCommand) const
  {
    return GetNextWindow(mHandle, iGWCommand);
  }

  //! Returns the thread and process identifiers of this window
  SPLINL DWORD getThreadProcessId(PDWORD oProcessId = NULL) const
  {
    return GetWindowThreadProcessId(mHandle, oProcessId);
  }

  //! Returns the thread identifier of this window
  SPLINL DWORD getThreadId() const
  {
    return GetWindowThreadProcessId(mHandle, NULL);
  }

  //! Returns the process identifier of this window
  SPLINL DWORD getProcessId() const
  {
    DWORD wProcessId;
    GetWindowThreadProcessId(mHandle, &wProcessId);
    return wProcessId;
  }

  //! Gets window ID
  SPLINL INT getID() const
  {
    return (INT)GetWindowLongPtr(mHandle, GWLP_ID);
  }
  //! Sets window ID
  SPLINL VOID setID(INT iId)
  {
    SetWindowLongPtr(mHandle, GWLP_ID, (LONG_PTR)iId);
  }

  //! Gets window styles
  SPLINL DWORD getStyle() const
  {
    return (DWORD)GetWindowLongPtr(mHandle, GWL_STYLE);
  }
  //! Sets window styles
  SPLINL VOID setStyle(DWORD iWSStyle)
  {
    SetWindowLongPtr(mHandle, GWL_STYLE, (LONG)iWSStyle);
  }

  //! Gets window extended styles
  SPLINL DWORD getExStyle() const
  {
    return (DWORD)GetWindowLongPtr(mHandle, GWL_EXSTYLE);
  }
  //! Sets window extended styles
  SPLINL VOID setExStyle(DWORD iWSEXStyle)
  {
    SetWindowLongPtr(mHandle, GWL_EXSTYLE, (LONG)iWSEXStyle);
  }

  //! Gets window user data
  SPLINL PVOID getUserData() const
  {
    return (PVOID)GetWindowLongPtr(mHandle, GWLP_USERDATA);
  }
  //! Sets window user data
  SPLINL VOID setUserData(PVOID iData)
  {
    SetWindowLongPtr(mHandle, GWLP_USERDATA, (LONG)iData);
  }

  //! Gets window procedure
  SPLINL WNDPROC getWndProc() const
  {
    return (WNDPROC)GetWindowLongPtr(mHandle, GWLP_WNDPROC);
  }
  //! Sets window procedure
  SPLINL VOID setWndProc(WNDPROC iWndProc)
  {
    SetWindowLongPtr(mHandle, GWLP_WNDPROC, (LONG)iWndProc);
  }

  //! Gets window instance
  SPLINL HINSTANCE getInstance() const
  {
    return (HINSTANCE)GetWindowLongPtr(mHandle, GWLP_HINSTANCE);
  }
  //! Sets window instance
  SPLINL VOID setInstance(HINSTANCE iInstance)
  {
    SetWindowLongPtr(mHandle, GWLP_HINSTANCE, (LONG)iInstance);
  }

  //! Returns TRUE if window is created
  SPLINL BOOL isCreated() const
  {
    return IsWindow(mHandle);
  }

  //! Sets a window timer
  SPLINL UINT_PTR setTimer(UINT iTimerId, UINT iInterval)
  {
    return SetTimer(mHandle, iTimerId, iInterval, NULL);
  }
  //! Removes a window timer
  SPLINL BOOL removeTimer(UINT iTimerId)
  {
    return KillTimer(mHandle, iTimerId);
  }

  //! Shows window if hidden
  SPLINL BOOL show()
  {
    return ShowWindow(mHandle, SW_SHOW);
  }
  //! Hides window if showed
  SPLINL BOOL hide()
  {
    return ShowWindow(mHandle, SW_HIDE);
  }
  //! Shows window if hidden
  SPLINL BOOL showAsync()
  {
    return ShowWindowAsync(mHandle, SW_SHOW);
  }
  //! Hides window if showed
  SPLINL BOOL hideAsync()
  {
    return ShowWindowAsync(mHandle, SW_HIDE);
  }
  //! Maximizes window
  SPLINL BOOL maximize()
  {
    return ShowWindow(mHandle, SW_MAXIMIZE);
  }
  //! Minimizes window
  SPLINL BOOL minimize()
  {
    return ShowWindow(mHandle, SW_MINIMIZE);
  }
  //! Restores window if minimized or maximized
  SPLINL BOOL restore()
  {
    return ShowWindow(mHandle, SW_RESTORE);
  }
  //! Sets focus to this window
  SPLINL HWND    focus() const
  {
    return SetFocus(mHandle);
  }
  //! Switches to this window
  SPLINL VOID    switchTo() const
  {
    SwitchToThisWindow(mHandle, FALSE);
  }
  //! Brings this window to the top of the z-order
  SPLINL BOOL   bringToTop() const
  {
    return BringWindowToTop(mHandle);
  }
  //! Activates this window
  SPLINL HWND    activate() const
  {
    return SetActiveWindow(mHandle);
  }

  //! Returns TRUE if window is visible
  SPLINL BOOL isVisible() const
  {
    return IsWindowVisible(mHandle);
  }
  //! Returns TRUE if window is maximized
  SPLINL BOOL isMaximized() const
  {
    return IsZoomed(mHandle);
  }
  //! Returns TRUE if window is minimized
  SPLINL BOOL isMinimized() const
  {
    return IsIconic(mHandle);
  }
  //! Returns TRUE if child of the given window
  SPLINL BOOL isChildOf(HWND iWindow) const
  {
    return IsChild(iWindow, mHandle);
  }
  //! Returns TRUE if this is the active window
  SPLINL BOOL isActive() const
  {
    return GetActiveWindow() == mHandle;
  }
  //! Returns TRUE if this window is focused
  SPLINL BOOL isFocused() const
  {
    return GetFocus() == mHandle;
  }

  //! Returns the top child window
  SPLINL HWND getTopChild() const
  {
    return GetTopWindow(mHandle);
  }
  //! Finds the child window located at the given point
  SPLINL HWND getChild(const POINT iPoint) const
  {
    return ChildWindowFromPoint(mHandle, iPoint);
  }
  //! Finds the child window located at the given point
  SPLINL HWND getChild(const POINT iPoint, UINT iCWPFlags) const
  {
    return ChildWindowFromPointEx(mHandle, iPoint, iCWPFlags);
  }

  //! Brings this window thread to the foreground and activates this window
  SPLINL BOOL setForeground() const
  {
    return SetForegroundWindow(mHandle);
  }
  //! Returns the foreground window
  static SPLINL HWND getForeground()
  {
    return GetForegroundWindow();
  }

  //! Returns this window's restored bounds
  SPLINL VOID getRestoredRect(PRECT oRect) const
  {
    WINDOWPLACEMENT wPlacement;
    GetWindowPlacement(mHandle, &wPlacement);
    *oRect = wPlacement.rcNormalPosition;
  }

  //! Returns this window's client area coordinates
  SPLINL Geom::Rect getClient() const
  {
    Geom::Rect wRect;
    getClient(&wRect);
    return wRect;
  }
  //! Returns this window's client area coordinates
  SPLINL BOOL getClient(PRECT oRect) const
  {
    return GetClientRect(mHandle, oRect);
  }
  //! Returns this window's client area coordinates
  SPLINL VOID getClient(Geom::BOUNDS& oBounds) const
  {
    Geom::Rect wRect;
    getClient(&wRect);

    oBounds = Geom::Bounds(&wRect);
  }
  //! Returns this window's client area coordinates
  SPLINL VOID getClient(Gdip::Rect& oBounds) const
  {
    Geom::Rect wRect;
    getClient(&wRect);

    oBounds = Gdip::Rect(
      wRect.left,
      wRect.top,
      wRect.getWidth(),
      wRect.getHeight());
  }

  //! Returns this window's bounds
  SPLINL Geom::Rect getRect() const
  {
    Geom::Rect wRect;
    getRect(&wRect);
    return wRect;
  }
  //! Returns this window's bounds
  SPLINL BOOL getRect(PRECT oRect) const
  {
    return GetWindowRect(mHandle, oRect);
  }

  //! Returns window bounds
  SPLINL Geom::Bounds getBounds() const
  {
    Geom::Rect wRect;
    GetWindowRect(mHandle, &wRect);

    return Geom::Bounds(&wRect);
  }

  //! Returns window bounds
  SPLINL VOID getBounds(Geom::Bounds& oBounds) const
  {
    Geom::Rect wRect;
    GetWindowRect(mHandle, &wRect);

    oBounds = Geom::Bounds(&wRect);
  }

  //! Returns window bounds
  SPLINL VOID getBounds(Gdip::Rect& oBounds) const
  {
    Geom::Rect wRect;
    GetWindowRect(mHandle, &wRect);

    oBounds = Gdip::Rect(
      wRect.left,
      wRect.top,
      wRect.getWidth(),
      wRect.getHeight());
  }

  //! Returns window bounds
  SPLINL SIZE getSize() const
  {
    return getBounds().size;
  }

  //! Returns window bounds
  SPLINL POINT getPos() const
  {
    return getBounds().point;
  }

  //! Returns this window's restored bounds
  SPLINL RECT getRestoredRect() const
  {
    WINDOWPLACEMENT wp;
    GetWindowPlacement(mHandle, &wp);
    return wp.rcNormalPosition;
  }

  //! Changes the position and dimensions of this window
  SPLINL BOOL setBounds(INT x, INT y, INT cx, INT cy)
  {
    return SetWindowPos(
      mHandle, NULL,
      x, y, cx, cy,
      SWP_NOZORDER);
  }
  //! Changes the position and dimensions of this window
  SPLINL BOOL setBounds(const Geom::BOUNDS& iBounds)
  {
    return SetWindowPos(
      mHandle, NULL,
      iBounds.x, iBounds.y, iBounds.cx, iBounds.cy,
      SWP_NOZORDER);
  }
  //! Sets window bounds
  SPLINL BOOL setBounds(const POINT& iPosition, const SIZE& iSize)
  {
    return SetWindowPos(mHandle, NULL,
      iPosition.x, iPosition.y, iSize.cx, iSize.cy, SWP_NOZORDER);
  }
  //! Sets window client bounds
  SPLINL BOOL setClientBounds(const Geom::BOUNDS& iBounds)
  {
    Geom::Rect wRect(iBounds);

    adjustRect(&wRect);
    return setBounds(Geom::Bounds(&wRect));
  }

  //! Defers the position and dimensions of this window
  SPLINL HDWP deferBounds(HDWP iDWP, INT x, INT y, INT cx, INT cy)
  {
    return DeferWindowPos(
      iDWP, mHandle, NULL,
      x, y, cx, cy,
      SWP_NOZORDER);
  }
  //! Defers the position and dimensions of this window
  SPLINL HDWP deferBounds(HDWP iDWP, const Geom::BOUNDS& iBounds)
  {
    return DeferWindowPos(
      iDWP, mHandle, NULL,
      iBounds.x, iBounds.y, iBounds.cx, iBounds.cy,
      SWP_NOZORDER);
  }

  //! Changes the size of this window
  SPLINL BOOL setSize(INT cx, INT cy)
  {
    return SetWindowPos(mHandle, NULL,
      0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER);
  }
  //! Changes the size of this window
  SPLINL BOOL setSize(const SIZE& iSize)
  {
    return SetWindowPos(mHandle, NULL,
      0, 0, iSize.cx, iSize.cy, SWP_NOMOVE | SWP_NOZORDER);
  }
  //! Sets window client size
  SPLINL BOOL setClientSize(INT cx, INT cy)
  {
    Geom::Rect rect(0, 0, cx, cy);

    adjustRect(&rect);
    return setSize(rect.getWidth(), rect.getHeight());
  }
  //! Sets window client size
  SPLINL BOOL setClientSize(const SIZE& iSize)
  {
    return setClientSize(iSize.cx, iSize.cy);
  }

  //! Changes the position of this window
  SPLINL BOOL setPos(INT x, INT y)
  {
    return SetWindowPos(
      mHandle,
      NULL,
      x,
      y,
      0,
      0,
      SWP_NOSIZE | SWP_NOZORDER);
  }
  //! Changes the position of this window
  SPLINL BOOL setPos(const POINT& iPosition)
  {
    return SetWindowPos(
      mHandle,
      NULL,
      iPosition.x,
      iPosition.y,
      0,
      0,
      SWP_NOSIZE | SWP_NOZORDER);
  }

  //! Centers the window over the screen
  SPLINL BOOL centerToScreen()
  {
    Geom::Rect rect = getRect();

    return setPos(
      (GetSystemMetrics(SM_CXSCREEN) - rect.getWidth()) / 2,
      (GetSystemMetrics(SM_CYSCREEN) - rect.getHeight()) / 2);
  }

  //! Changes the parent of this window
  //! Returns:            Last parent window
  SPLINL HWND setParent(HWND iParent)
  {
    return SetParent(mHandle, iParent);
  }
  //! Returns the parent window
  SPLINL HWND getParent() const
  {
    return GetParent(mHandle);
  }

  //! Changes this window size, position and z-order
  SPLINL BOOL setPosition(
    HWND iInsertAfter,
    INT x,
    INT y,
    INT cx,
    INT cy,
    UINT iFlags
    )
  {
    return SetWindowPos(
      mHandle, iInsertAfter,
      x, y, cx, cy,
      iFlags);
  }
  //! Changes this window size, position and z-order
  SPLINL BOOL setPosition(
    HWND iInsertAfter,
    const Geom::BOUNDS& iBounds,
    UINT iFlags
    )
  {
    return SetWindowPos(
      mHandle, iInsertAfter,
      iBounds.x, iBounds.y, iBounds.cx, iBounds.cy,
      iFlags);
  }

  //! Changes this window's text
  SPLINL BOOL setText(PCTSTR iText)
  {
    return SetWindowText(mHandle, iText);
  }
  //! Gets this window's text
  SPLINL BOOL getText(PTSTR iText, INT iMaxCount) const
  {
    return GetWindowText(mHandle, iText, iMaxCount);
  }

  //! Gets this window's text length
  SPLINL INT getTextLength() const
  {
    return GetWindowTextLength(mHandle);
  }
  //! Gets this window's text
  //! Return value must be deleted by caller
  SPLINL PTSTR getText() const
  {
    INT nLength = getTextLength() + 1;
    PTCHAR szText = new TCHAR[nLength];

    if (!getText(szText, nLength)) szText[0] = '\0';
    return szText;
  }

  //! Gets window font
  SPLINL HFONT getFont() const
  {
    return GetWindowFont(mHandle);
  }
  //! Sets window font
  SPLINL VOID setFont(HFONT iFont, BOOL iRedraw = TRUE)
  {
    SetWindowFont(mHandle, iFont, iRedraw);
  }

  //! Invalidates window
  SPLINL BOOL invalidate(const PRECT iRect, BOOL iErase = TRUE)
  {
    return InvalidateRect(mHandle, iRect, iErase);
  }
  //! Invalidates window
  SPLINL BOOL invalidate(const Gdip::Rect& iBounds, BOOL iErase = TRUE)
  {
    return InvalidateRect(
      mHandle,
      &Geom::Rect(
      iBounds.X,
      iBounds.Y,
      iBounds.X + iBounds.Width,
      iBounds.Y + iBounds.Height
      ),
      iErase);
  }
  //! Invalidates window
  SPLINL BOOL invalidate(const Geom::BOUNDS& iBounds, BOOL iErase = TRUE)
  {
    return InvalidateRect(mHandle, &Geom::Rect(iBounds), iErase);
  }
  //! Invalidates window
  SPLINL BOOL invalidate(BOOL iErase = TRUE)
  {
    return InvalidateRect(mHandle, NULL, iErase);
  }

  //! Paints invalidated window region
  SPLINL BOOL update()
  {
    return UpdateWindow(mHandle);
  }

  //! Gets window-relative coordinates
  SPLINL BOOL pointToClient(POINT& ioPoint) const
  {
    return ScreenToClient(mHandle, &ioPoint);
  }
  //! Gets screen-relative coordinates
  SPLINL BOOL pointToScreen(POINT& ioPoint) const
  {
    return ClientToScreen(mHandle, &ioPoint);
  }

  //! Enables window
  SPLINL BOOL enable()
  {
    return EnableWindow(mHandle, TRUE);
  }
  //! Disables window
  SPLINL BOOL disable()
  {
    return EnableWindow(mHandle, FALSE);
  }
  //! Gets window enable state
  SPLINL BOOL isEnabled() const
  {
    return IsWindowEnabled(mHandle);
  }

  //! Gets owner window
  SPLINL HWND getOwner() const
  {
    return GetWindowOwner(mHandle);
  }

  //! Gets window menu
  SPLINL HMENU getMenu() const
  {
    return GetMenu(mHandle);
  }
  //! Sets window menu
  SPLINL BOOL setMenu(HMENU iMenu)
  {
    return SetMenu(mHandle, iMenu);
  }

  //! Adjusts rect to include frame dimensions
  SPLINL BOOL adjustRect(RECT* iRect) const
  {
    return AdjustWindowRect(iRect, getStyle(), getMenu() > 0);
  }
  //! Adjusts bounds to include frame dimensions
  SPLINL BOOL adjustBounds(Geom::Bounds& ioBounds) const
  {
    Geom::Rect wRect(ioBounds);
    BOOL wResult = adjustRect(&wRect);

    ioBounds = Geom::Bounds(&wRect);
    return wResult;
  }

  //! Flashes window
  SPLINL BOOL flash(BOOL iFlash = TRUE) const
  {
    return FlashWindow(mHandle, iFlash);
  }

  //! Sets small window icon
  SPLINL LRESULT setIconSmall(HANDLE iIcon)
  {
    return SendMessage(mHandle, WM_SETICON, ICON_SMALL, (LPARAM)iIcon);
  }
  //! Sets large window icon
  SPLINL LRESULT setIconBig(HANDLE iIcon)
  {
    return SendMessage(mHandle, WM_SETICON, ICON_BIG, (LPARAM)iIcon);
  }

  //! Changes window theme
  SPLINL HRESULT setTheme(PCTSTR iAppName, PCTSTR iIdList = NULL)
  {
    return SetWindowTheme(mHandle, iAppName, iIdList);
  }

  //! Shows a message dialog
  SPLINL INT showMessage(PCTSTR iTitle, PCTSTR iMessage, UINT iType = MB_OK) const
  {
    return MessageBox(mHandle, iMessage, iTitle, iType);
  }

  //! Shows a message dialog
  SPLINL INT showMessage(PCTSTR iMessage, UINT iType = MB_OK) const
  {
    TCHAR wTitle[100];
    getText(wTitle, ARRAYSIZE(wTitle));
    return MessageBox(mHandle, iMessage, wTitle, iType);
  }

  //! Returns window class name
  SPLINL INT getClassName(PTSTR oName, INT iLength) const
  {
    return GetClassName(mHandle, oName, iLength);
  }

  //! Begins painting on this window
  SPLINL HDC beginPaint(PPAINTSTRUCT oPaint)
  {
    return BeginPaint(mHandle, oPaint);
  }

  //! Ends painting on this window
  SPLINL BOOL endPaint(PPAINTSTRUCT iPaint)
  {
    return EndPaint(mHandle, iPaint);
  }

  //! Returns window handle
  SPLINL HWND getHandle()
  {
    return mHandle;
  }
  //! Returns window handle
  SPLINL operator HWND()
  {
    return mHandle;
  }
  //! Returns window bounds
  SPLINL operator Geom::Rect() const
  {
    return Geom::Rect::fromWindow(mHandle);
  }
  //! Returns window bounds
  SPLINL operator Geom::Bounds() const
  {
    return Geom::Bounds::fromWindow(mHandle);
  }
  //! Returns window position
  SPLINL operator Geom::Point() const
  {
    return Geom::Point::fromWindow(mHandle);
  }
  //! Returns window creation state
  SPLINL operator BOOL() const
  {
    return isCreated();
  }
#pragma endregion

#pragma region Tools
  //! Adjust rectangle to fit window minimum size
  //! Remarks: minimum size does not include frame dimensions
  SPLINL VOID adjustMinimumSize(
    INT iMinWidth,
    INT iMinHeight,
    UINT_PTR iEdge,
    PRECT iRect
    ) const
  {
    Geom::Rect wMinRect(0, 0, iMinWidth, iMinHeight);
    adjustRect(&wMinRect);

    INT wWidth = max(iRect->right - iRect->left, wMinRect.getWidth());
    INT wHeight = max(iRect->bottom - iRect->top, wMinRect.getHeight());

    if (iEdge == WMSZ_LEFT ||
        iEdge == WMSZ_TOPLEFT ||
        iEdge == WMSZ_BOTTOMLEFT)
    {
      iRect->left += iRect->right - iRect->left - wWidth;
    }

    if (iEdge == WMSZ_TOP ||
        iEdge == WMSZ_TOPLEFT ||
        iEdge == WMSZ_TOPRIGHT)
    {
      iRect->top += iRect->bottom - iRect->top - wHeight;
    }

    iRect->right = iRect->left + wWidth;
    iRect->bottom = iRect->top + wHeight;
  }

#pragma endregion

#pragma region Scrolling
  VOID scrollPoint(POINT& point, ScrollInfo& scroll);
  VOID scrollToPoint(const POINT& point, ScrollInfo& scroll);
  VOID updateScroll(const SIZE& content, ScrollInfo& scroll);
  VOID setScrollX(INT value, ScrollInfo& scroll);
  VOID setScrollY(INT value, ScrollInfo& scroll);
#pragma endregion

#pragma region Notify Icon
  //! Creates a notify icon associated with this window class
  //! Remarks:         the notify icon is not destroyed with the class
  //!                  you need to call destroyNotifyIcon to release
  //!                  the allocated resources
  //! hIcon:           handle of the icon to display
  //! szTip:           tooltip text
  //! uID:             notify icon identifier
  BOOL createNotifyIcon(
    HICON hIcon,
    PCTSTR szTip,
    UINT uID = DEFAULT_NOTIFYID
    );

  //! Creates a notify icon associated with this window class
  //! Remarks:         the notify icon is not destroyed with the class
  //!                  you need to call destroyNotifyIcon to release
  //!                  the allocated resources
  //! nIDIcon:         resource identifier of the icon to display
  //! szTip:           tooltip text
  //! uID:             notify icon identifier
  SPLINL BOOL createNotifyIcon(
    int nIDIcon,
    PCTSTR szTip,
    UINT uID = DEFAULT_NOTIFYID
    )
  {
    return createNotifyIcon(
      LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(nIDIcon)),
      szTip, uID);
  }

  //! Destroys a notify icon
  //! uID:             identifier of the notify icon to destroy
  BOOL destroyNotifyIcon(
    UINT uID = DEFAULT_NOTIFYID
    );

  //! Notifies the user about an event
  //! szInfo:          message to display
  //! szTitle:         message title
  //! uID:             notify icon identifier
  //! dwInfoFlags:     message attributes (any NIIF_* value)
  //! hIcon:           message icon (if NIIF_USER attribute set)
  BOOL notify(
    PCTSTR szInfo,
    PCTSTR szTitle,
    UINT uID = DEFAULT_NOTIFYID,
    DWORD dwInfoFlags = NIIF_INFO,
    HICON hIcon = NULL
    );
#pragma endregion

#pragma region Friends
  friend class WndBase;
  friend class FrameWnd;
  friend class Dialog;
  friend class Window;
  friend class HiddenWnd;
#pragma endregion
};

#pragma warning(default:4065)

#endif